package Locating;

import prefuse.data.Tuple;
import Tracing.EnvironmentStack;
import Tracing.FixEquationStack;
import Tracing.HistoryStack;
import Tracing.State;
import Tracing.Trace;
import UI.Constants;
import UI.Utility;
import UI.View;

public class Locate {
	
	public Locate(Trace trace){
		
		this.trace = trace;

		this.fixequationStack = new FixEquationStack();
		
		step = 1;

	}
	
	public Locate(Trace trace, View view){
		
		this(trace);
		
		this.view = view;
	}
	
	public void initial(){
		
		
		this.breakVariable = trace.getBreakVariable();
		
		this.historyStack = trace.getHistoryStack();
		
		this.expectedVariableValue = trace.getBreakValue();
		
		this.type = findVariables;
		
	}
	
	public void executeStepByStep(HistoryStack historyStack, String breakvariable){
		
		int top = historyStack.getRowCount()-1;
		if(top > -1){
			view.getRulePanel().getTextArea().append("Locating step "+ step++ + ":\n");
		
			//System.out.println("!!!!!!");
			//Step 3

			State  oldSigma = (State)historyStack.get(top, "state");
			String oldExpression = historyStack.getString(top, "expression");
			String oldClause = historyStack.getString(top, "variable") + ":=" + oldExpression;
		
			if(type == findVariables){
				while(historyStack.getRowCount()>0){
					if(!historyStack.getString(historyStack.getRowCount()-1, "variable").equals(breakvariable)){
						historyStack.pop();
					}else{
						//System.out.println(historyStack.getRowCount());
						System.out.println("|\n|Locate Rule Variables\n|");

						State newSigma = (State)historyStack.get(historyStack.getRowCount()-1,"state");
						String value = oldSigma.execute(oldExpression);
						String newClause = historyStack.getString(historyStack.getRowCount()-1, "variable") + ":=" + historyStack.getString(historyStack.getRowCount()-1, "expression");
						trace.getView().getRulePanel().getTextArea().append(Utility.getVariableLocatingRule(breakvariable, value, expectedVariableValue, newClause, oldSigma, newSigma));
						break;
					}
				}
				historyStack.print();
				historyStack.print(trace.getView());
				this.currentLine = historyStack.getString(historyStack.getRowCount()-1, "Line");
				type = findConditions;
			}else{
				
				State expectedSigma = (State)historyStack.get(top, "Estate");
				EnvironmentStack epsilon = (EnvironmentStack)historyStack.get(top, "Benvironment");
		
				int epsilonSize = epsilon.getRowCount();;
				String expectedBooleanValue = null;
				String condition = null;
				State epsilonState = new State();
				State expectedEpsilonState = new State();
				
				if(epsilonSize==0){
					this.currentLine = historyStack.getString(historyStack.getRowCount()-1, "Line");
					type = getFixequations;
				}else{
					expectedBooleanValue = epsilon.getString(epsilonSize-1, "Evalue");
					condition = epsilon.getString(epsilonSize-1, "expression");
					epsilonState = (State)epsilon.get(epsilonSize-1,"state");
					expectedEpsilonState = (State)epsilon.get(epsilonSize-1, "Estate");
				}
			
				if(type == findConditions){
					/*
					while(epsilon.getRowCount()>0){
						epsilonSize = epsilon.getRowCount();
						expectedBooleanValue = epsilon.getString(epsilonSize-1, "Evalue");
						condition =  epsilon.getString(epsilonSize-1, "expression");
						epsilonState = (State)epsilon.get(epsilonSize-1,"state");
						String evalueCondition = epsilonState.execute(condition);
						if((expectedBooleanValue!=null&&expectedBooleanValue.equals(evalueCondition))||expectedBooleanValue==null){
							epsilon.pop();
							System.out.println("|\n|Locate Rule Block 4.5\n|");
							trace.getView().getRulePanel().getTextArea().append(Utility.getConditionLocatingRule(condition, epsilonState, oldClause, oldSigma, expectedBooleanValue));
							type = findConditions;
						}else{
							this.currentLine = epsilon.getString(epsilonSize-1, "Line");
							break;
						}
					}
					historyStack.print();
					historyStack.print(trace.getView());
					type = getFixequations;
					*/
					if(epsilon.getRowCount()>0){
						epsilonSize = epsilon.getRowCount();
						expectedBooleanValue = epsilon.getString(epsilonSize-1, "Evalue");
						condition =  epsilon.getString(epsilonSize-1, "expression");
						epsilonState = (State)epsilon.get(epsilonSize-1,"state");
						String evalueCondition = epsilonState.execute(condition);
						if((expectedBooleanValue!=null&&expectedBooleanValue.equals(evalueCondition))||expectedBooleanValue==null){
							this.currentLine = epsilon.getString(epsilonSize-1, "Line");
							epsilon.pop();
							System.out.println("|\n|Locate Rule Block 4.5\n|");
							trace.getView().getRulePanel().getTextArea().append(Utility.getConditionLocatingRule(condition, epsilonState, oldClause, oldSigma, expectedBooleanValue));
							type = findConditions;
						}else{
							this.currentLine = epsilon.getString(epsilonSize-1, "Line");
							type = getFixequations;
						}
					}else{
						type = getFixequations;
					}
					historyStack.print();
					historyStack.print(trace.getView());
				}else{
					//System.out.println("B environment size : " + epsilon.getRowCount());
					if(type == getFixequations){
						
						//Environment is not empty
						if(epsilon.getRowCount()>0){
							Tuple tuple = epsilonState.compareWith(expectedEpsilonState);
							if(tuple!=null){
								//some variable is wrong
								String newBreakVariable = tuple.getString("variable");
								String newBreakValue = tuple.getString("value");
								System.out.println("|\n|Locate Rule Block 4.6\n|");
								int nextRow = this.getRowWithVariable(historyStack, newBreakVariable);
								String newClause = null;
								State newSigma = null;
								if(nextRow!=-1){
									newClause = historyStack.getString(nextRow, "variable") + ":=" + historyStack.getString(nextRow, "expression");
									newSigma = ((State)historyStack.get(nextRow, "state")).copy();
								}
								trace.getView().getRulePanel().getTextArea().append(Utility.getConditionVariableFixRule(newBreakVariable, condition, newBreakValue, oldClause, expectedBooleanValue, newClause, epsilonState, oldSigma, newSigma));
								fixequationStack.push(epsilonState.replaceExcept(condition, newBreakVariable)+ "=" + expectedBooleanValue);
								fixequationStack.print(view);

					
								this.breakVariable = newBreakVariable;
					
								this.expectedVariableValue = newBreakValue;
					
								this.type = findVariables;
					
								this.currentLine = epsilon.getString(epsilonSize-1, "Line");
					
								this.type = findVariables;
					
					
							}else{
								//the operations of condition are wrong
								StringBuffer buf = new StringBuffer();
								int count = expectedEpsilonState.getRowCount();
								buf.append(Constants.lbrack + "g(");
								/*for(int i = 0 ; i < count; i++){
									buf.append(expectedEpsilon.getString(i, "variable")+",");
								}*/
								for(int i = 0 ; i < epsilonState.getRowCount(); i++){
									if(condition.indexOf(epsilonState.getString(i, "variable"))!=-1){
										buf.append(epsilonState.getString(i, "variable")+",");
									}
								}
								buf.replace(buf.lastIndexOf(","), buf.length(), ")");
								buf.append(Constants.rbrack+epsilonState.toString()+"="+expectedBooleanValue);
								fixequationStack.push(buf.toString());
								System.out.println("|\n|Locate Rule Block 4.7\n|");
								trace.getView().getRulePanel().getTextArea().append(Utility.getConditionOperationFixRule(condition, oldClause, expectedBooleanValue, oldSigma, epsilonState));
								fixequationStack.print(view);
					
								//termination
								type = -1;
							}
				
						}else{
							Tuple tuple = oldSigma.compareWith(expectedSigma);
							//environment is empty
							if(tuple!=null){
								//some variable's value is wrong
								String newBreakVariable = tuple.getString("variable");
								String newBreakValue = tuple.getString("value");
								//fixequationStack.push(oldSigma.replaceExcept(oldExpression, newBreakVariable)+"="+expectedevalue);
								System.out.println("|\n|Locate Rule Assignment 4.3\n|");
								int nextRow = this.getRowWithVariable(historyStack, newBreakVariable);
								String newClause = null;
								State newSigma = null;
								if(nextRow!=-1){
									newClause = historyStack.getString(nextRow, "variable");
									newSigma = ((State)historyStack.get(nextRow, "state")).copy();
								}
								trace.getView().getRulePanel().getTextArea().append(Utility.getAssignmentVariableFixRule(newBreakVariable, oldExpression, newBreakValue, oldClause, expectedVariableValue, newClause, oldSigma, newSigma));
								fixequationStack.push(oldSigma.replaceExcept(oldExpression, newBreakVariable)+"="+expectedVariableValue);
								fixequationStack.print(view);
								
								
								//in case of the situation of x:=x+1;
								historyStack.pop();
					
								this.breakVariable = newBreakVariable;
					
								this.expectedVariableValue = newBreakValue;
					
								this.type = findVariables;
					
								this.currentLine = historyStack.getString(top, "Line");
					
								this.type = findVariables;
							}else{
								//the operations of the expression is wrong
								StringBuffer buf = new StringBuffer();
								//int count = expectedSigma.getRowCount();
								//String var = historyStack.getString(top,"variable");
									buf.append(Constants.lbrack + "f(");
						
									//ensure that the arguments of fix equation are from the current expression
									for(int i = 0 ; i < oldSigma.getRowCount(); i++){
										if(oldExpression.indexOf(oldSigma.getString(i, "variable"))!=-1){
											buf.append(oldSigma.getString(i, "variable")+",");
										}
									}
									if(buf.lastIndexOf(",")!=-1){
									buf.replace(buf.lastIndexOf(","), buf.length(), ")");
									}else{
										buf.append(")");
									}
									buf.append(Constants.rbrack+oldSigma.toString()+"="+(expectedSigma.getValue(breakVariable)==null?expectedVariableValue:expectedSigma.getValue(breakVariable)));
									fixequationStack.push(buf.toString());
									System.out.println("|\n|Locate Rule Assignment 4.4\n|");
									trace.getView().getRulePanel().getTextArea().append(Utility.getAssignmentOperationFixRule(oldExpression, oldClause, expectedVariableValue, oldSigma));
									fixequationStack.print(view);
								
								//termination
								type = -1;
							}
						}
					}	
				}
			}
		}
	}
	
	public void execute(HistoryStack historyStack, String breakvariable, String expectedExpValue){
	
		//System.out.println("!!!!!!");
		//Step 3
		while(historyStack.getRowCount()>0){
			if(!historyStack.getString(historyStack.getRowCount()-1, "variable").equals(breakvariable)){
				historyStack.pop();
			}else{
				//System.out.println(historyStack.getRowCount());
				System.out.println("|\n|Locate Rule Variables\n|");
				trace.getView().getRulePanel().getTextArea().setText("|\n|Locate Rule Variables\n|");
				break;
			}
		}
		
		historyStack.print();
		historyStack.print(trace.getView());
		
		int top = historyStack.getRowCount()-1;
		if(top>-1){
			State sigma = (State)historyStack.get(top, "state");
			State expectedSigma = (State)historyStack.get(top, "Estate");
			String expression = historyStack.getString(top, "expression");
			EnvironmentStack epsilon = (EnvironmentStack)historyStack.get(top, "Benvironment");
		
			int size = 0;
			String expectedBooleanValue = null;
			String condition = null;
			State state = null;
			while(epsilon.getRowCount()>0){
				size = epsilon.getRowCount();
				expectedBooleanValue = epsilon.getString(size-1, "Evalue");
				condition =  epsilon.getString(size-1, "expression");
				state = (State)epsilon.get(size-1,"state");
				String evalueCondition = state.execute(condition);
				if(expectedBooleanValue!=null&&expectedBooleanValue.equals(evalueCondition)){
					epsilon.pop();
					System.out.println("|\n|Locate Rule Block 4.5\n|");
					trace.getView().getRulePanel().getTextArea().append("|\n|Locate Rule Block 4.5\n|");
				}else{
					break;
				}
			}
		
			//System.out.println("B environment size : " + epsilon.getRowCount());
		
			//Environment is not empty
			if(epsilon.getRowCount()>0){
				State expectedEpsilon = (State)epsilon.get(size-1, "Estate");
				Tuple tuple = state.compareWith(expectedEpsilon);
				if(tuple!=null){
					//some variable is wrong
					String newBreakVariable = tuple.getString("variable");
					fixequationStack.push(state.replaceExcept(condition, newBreakVariable)+ "=" + expectedBooleanValue);
					System.out.println("|\n|Locate Rule Block 4.6\n|");
					trace.getView().getRulePanel().getTextArea().append("|\n|Locate Rule Block 4.6\n|");
					execute(historyStack, newBreakVariable, expectedExpValue);
				}else{
					//the operations of condition are wrong
					StringBuffer buf = new StringBuffer();
					int count = expectedEpsilon.getRowCount();
					buf.append(Constants.lbrack + "g(");
					/*for(int i = 0 ; i < count; i++){
						buf.append(expectedEpsilon.getString(i, "variable")+",");
					}*/
					for(int i = 0 ; i < state.getRowCount(); i++){
						if(condition.indexOf(state.getString(i, "variable"))!=-1){
							buf.append(state.getString(i, "variable")+",");
						}
					}
					buf.replace(buf.lastIndexOf(","), buf.length(), ")");
					buf.append(Constants.rbrack +state.toString()+"="+expectedBooleanValue);
					fixequationStack.push(buf.toString());
					System.out.println("|\n|Locate Rule Block 4.7\n|");
					trace.getView().getRulePanel().getTextArea().append("|\n|Locate Rule Block 4.7\n|");
				}
				
			}else{
				//System.out.println(sigma.toString() + expectedSigma);
				Tuple tuple = sigma.compareWith(expectedSigma);
				if(tuple!=null){
					//some variable's value is wrong
					String newBreakVariable = tuple.getString("variable");
					String expectedevalue = tuple.getString("value");
					String exp = historyStack.getString(top, "expression");
					fixequationStack.push(sigma.replaceExcept(exp, newBreakVariable)+"="+expectedevalue);
					System.out.println("|\n|Locate Rule Assignment 4.3\n|");
					trace.getView().getRulePanel().getTextArea().append("|\n|Locate Rule Assignment 4.3\n|");
					execute(historyStack, newBreakVariable, expectedevalue);
				}else{
				
					//the operations of the expression is wrong
					StringBuffer buf = new StringBuffer();
					//int count = expectedSigma.getRowCount();
					String var = historyStack.getString(top,"variable");
					buf.append(Constants.lbrack + "g(");
					/*
						if(!expectedSigma.getString(i, "variable").equals(var)){
							buf.append(expectedSigma.getString(i, "variable")+",");
						}
					}*/
					//ensure that the arguments of fix equation are from the current expression
					for(int i = 0 ; i < sigma.getRowCount(); i++){
						if(expression.indexOf(sigma.getString(i, "variable"))!=-1){
							buf.append(sigma.getString(i, "variable")+",");
						}
					}
					buf.replace(buf.lastIndexOf(","), buf.length(), ")");
					buf.append(Constants.rbrack + sigma.toString()+"="+(expectedSigma.getValue(var)==null?expectedExpValue:expectedSigma.getValue(var)));
					fixequationStack.push(buf.toString());
					System.out.println("|\n|Locate Rule Assignment 4.4\n|");
					trace.getView().getRulePanel().getTextArea().append("|\n|Locate Rule Assignment 4.4\n|");
				}
			
			}	
		}
		
	}
	
	public FixEquationStack getFixequationStack() {
		return fixequationStack;
	}

	public void setFixequationStack(FixEquationStack fixequationStack) {
		this.fixequationStack = fixequationStack;
	}

	private Trace trace;
	private HistoryStack historyStack;
	private String breakVariable;
	public HistoryStack getHistoryStack() {
		return historyStack;
	}

	public void setHistoryStack(HistoryStack historyStack) {
		this.historyStack = historyStack;
	}

	public String getBreakVariable() {
		return breakVariable;
	}

	public void setBreakVariable(String breakVariable) {
		this.breakVariable = breakVariable;
	}

	public String getExpectedVariableValue() {
		return expectedVariableValue;
	}

	public void setExpectedVariableValue(String expectedValue) {
		this.expectedVariableValue = expectedValue;
	}

	public String getCurrentLine() {
		return currentLine;
	}

	public void setCurrentLine(String currentLine) {
		this.currentLine = currentLine;
	}


	private FixEquationStack fixequationStack;
	private String currentLine;
	private int type;
	private String expectedVariableValue;
	
	public int getType() {
		return type;
	}

	public void setType(int type) {
		this.type = type;
	}

	private View view;
	private int step;
	
	private static int findVariables = 0;
	private static int findConditions = 1;
	private static int getFixequations= 2;
	
	public int getRowWithVariable(HistoryStack hs, String var){
		int result = -1;
		for(int i = hs.getRowCount()-1; i > -1; i--){
			if(hs.getString(i, "variable").equals(var)){
				result = i;
				break;
			}
		}
		
		
		return result;
	}

}
